//+------------------------------------------------------------------+
//|                                 CM_Ultimate_MA_MTF_V2_MQL5.mq5 |
//|                      Copyright 2023, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.01"
#property description "MQL5 version of CM_Ultimate_MA_MTF_V2 by ChrisMoody. Fixed const correctness."

#property indicator_chart_window
#property indicator_buffers 7
#property indicator_plots   3

//--- Plot1 MA1
#property indicator_label1  "MA1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrLime,clrRed,clrAqua
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- Plot2 MA2
#property indicator_label2  "MA2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrLime,clrRed,clrAqua
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- Plot3 Cross
#property indicator_label3 "Cross"
#property indicator_type3  DRAW_ARROW
#property indicator_color3 clrAqua
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2

//--- Include library for MAs
#include <MovingAverages.mqh>

//--- Enums
enum ENUM_MA_TYPE
  {
   SMA = 1,
   EMA = 2,
   WMA = 3,
   HullMA = 4,
   VWMA = 5,
   RMA = 6, // Equivalent to SMMA in MT5
   TEMA = 7,
   TilsonT3 = 8
  };

//--- Inputs
input group "Common Settings"
input ENUM_APPLIED_PRICE   InpSourcePrice = PRICE_CLOSE; // Source price
input bool                 InpUseCurrentRes = true;      // Use Current Chart Resolution?
input ENUM_TIMEFRAMES      InpCustomRes   = PERIOD_D1;   // Use Different Timeframe?

input group "MA1 Settings"
input int                  InpLen1       = 20;           // Moving Average Length 1
input ENUM_MA_TYPE         InpMaType1    = SMA;          // Moving Average Type 1
input double               InpFactorT3_1 = 0.7;          // Tilson T3 Factor 1
input bool                 InpSpc1       = true;         // Show Price Crossing MA1 - Highlight Bar?
input bool                 InpCc1        = true;         // Change Color Based On Direction MA1?
input int                  InpSmoothe1   = 2;            // Color Smoothing 1

input group "MA2 Settings"
input bool                 InpShowMA2    = false;        // Show Optional 2nd Moving Average?
input int                  InpLen2       = 50;           // Moving Average Length 2
input ENUM_MA_TYPE         InpMaType2    = SMA;          // Moving Average Type 2
input double               InpFactorT3_2 = 0.7;          // Tilson T3 Factor 2
input bool                 InpSpc2       = false;        // Show Price Crossing MA2?
input bool                 InpCc2        = true;         // Change Color Based On Direction MA2?

input group "Cross Settings"
input bool                 InpShowCross  = true;         // Show Dots on Cross of Both MA's?

//--- Indicator Buffers
double ExtMA1Buffer[];
double ExtMA1ColorBuffer[];
double ExtMA2Buffer[];
double ExtMA2ColorBuffer[];
double ExtCrossBuffer[];
double ExtVwmaSrcVol[]; // Buffer for VWMA calculation
double ExtVwmaVol[];    // Buffer for VWMA calculation

//--- Global variables
ENUM_TIMEFRAMES    applied_timeframe;
int                vwma_handle = INVALID_HANDLE;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   Print("=== CM_Ultimate_MA_MTF_V2_MQL5 OnInit() started ===");
   
//--- Bind Buffers
   SetIndexBuffer(0, ExtMA1Buffer, INDICATOR_DATA);
   SetIndexBuffer(1, ExtMA1ColorBuffer, INDICATOR_COLOR_INDEX);
   PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, 3);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   SetIndexBuffer(2, ExtMA2Buffer, INDICATOR_DATA);
   SetIndexBuffer(3, ExtMA2ColorBuffer, INDICATOR_COLOR_INDEX);
   PlotIndexSetInteger(1, PLOT_COLOR_INDEXES, 3);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   if(!InpShowMA2) PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, Bars(_Symbol, _Period));


   SetIndexBuffer(4, ExtCrossBuffer, INDICATOR_DATA);
   PlotIndexSetInteger(2, PLOT_ARROW, 217); // Large X symbol
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   if(!InpShowCross) PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, Bars(_Symbol, _Period));

   SetIndexBuffer(5, ExtVwmaSrcVol, INDICATOR_CALCULATIONS);
   SetIndexBuffer(6, ExtVwmaVol, INDICATOR_CALCULATIONS);


//--- Set timeframe
   applied_timeframe = InpUseCurrentRes ? _Period : InpCustomRes;
   Print("Applied timeframe: ", EnumToString(applied_timeframe));
   
//--- Set labels
   PlotIndexSetString(0, PLOT_LABEL, "MA1(" + IntegerToString(InpLen1) + ")");
   if(InpShowMA2)
      PlotIndexSetString(1, PLOT_LABEL, "MA2(" + IntegerToString(InpLen2) + ")");
   else
      PlotIndexSetString(1, PLOT_LABEL, "");
   PlotIndexSetString(2, PLOT_LABEL, "Cross");

   Print("MA1 Type: ", InpMaType1, " Length: ", InpLen1);
   if(InpShowMA2) Print("MA2 Type: ", InpMaType2, " Length: ", InpLen2);
   Print("Show MA2: ", InpShowMA2, " Show Cross: ", InpShowCross);
   Print("=== OnInit() completed successfully ===");

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Update indicator label with current values                      |
//+------------------------------------------------------------------+
void UpdateIndicatorLabel()
  {
   string label_name = "CM_Ultimate_MA_Label";
   
   // Delete existing label
   ObjectDelete(0, label_name);
   
   // Create new label
   ObjectCreate(0, label_name, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, label_name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, label_name, OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0, label_name, OBJPROP_YDISTANCE, 25);
   ObjectSetInteger(0, label_name, OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(0, label_name, OBJPROP_FONTSIZE, 8);
   ObjectSetString(0, label_name, OBJPROP_FONT, "Arial Bold");
   ObjectSetInteger(0, label_name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, label_name, OBJPROP_HIDDEN, true);
   
   // Get current values
   int bars = Bars(_Symbol, _Period);
   double current_ma1 = ExtMA1Buffer[bars-1];
   double current_ma2 = InpShowMA2 ? ExtMA2Buffer[bars-1] : 0;
   double current_price = iClose(_Symbol, _Period, 0);
   
   // Create label text
   string label_text = "CM Ultimate MA MTF V2\n";
   label_text += "MA1(" + IntegerToString(InpLen1) + "): " + DoubleToString(current_ma1, _Digits) + "\n";
   if(InpShowMA2)
     {
      label_text += "MA2(" + IntegerToString(InpLen2) + "): " + DoubleToString(current_ma2, _Digits) + "\n";
     }
   label_text += "Price: " + DoubleToString(current_price, _Digits) + "\n";
   label_text += "TF: " + EnumToString(applied_timeframe);
   
   ObjectSetString(0, label_name, OBJPROP_TEXT, label_text);
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Print("=== OnDeinit() called, reason: ", reason, " ===");
   if(vwma_handle != INVALID_HANDLE)
      IndicatorRelease(vwma_handle);
   ObjectsDeleteAll(0, "CrossBG_");
   ObjectDelete(0, "CM_Ultimate_MA_Label");
   Print("=== OnDeinit() completed ===");
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   static datetime last_bar_time = 0;
   static bool first_run = true;
   
   if(first_run)
     {
      Print("=== OnCalculate() first run ===");
      Print("rates_total: ", rates_total, " prev_calculated: ", prev_calculated);
      Print("Current timeframe: ", EnumToString(_Period));
      first_run = false;
      last_bar_time = time[rates_total-1];
     }

   // Only calculate on new bar formation or timeframe change
   datetime current_bar_time = time[rates_total-1];
   if(current_bar_time == last_bar_time && prev_calculated > 0)
     {
      return(rates_total); // Skip calculation if no new bar
     }
   
   last_bar_time = current_bar_time;
   
   int start = prev_calculated > 1 ? prev_calculated - 1 : 0;
   
   if(rates_total < InpLen1)
     {
      Print("Warning: rates_total (", rates_total, ") < MA length (", InpLen1, ")");
      return(0);
     }

//--- Main calculation loop
   for(int i = start; i < rates_total && !IsStopped(); i++)
     {
      //--- Get MA values
      double ma1_value = GetMAValue(i, rates_total, applied_timeframe, InpMaType1, InpLen1, InpFactorT3_1, InpSourcePrice, tick_volume);
      ExtMA1Buffer[i] = ma1_value;
      
      if(InpShowMA2)
        {
         double ma2_value = GetMAValue(i, rates_total, applied_timeframe, InpMaType2, InpLen2, InpFactorT3_2, InpSourcePrice, tick_volume);
         ExtMA2Buffer[i] = ma2_value;
        }
      else
         ExtMA2Buffer[i] = EMPTY_VALUE;

      //--- Set colors
      if(InpCc1)
        {
         if(i > InpSmoothe1)
           {
            if(ExtMA1Buffer[i] > ExtMA1Buffer[i - InpSmoothe1])
               ExtMA1ColorBuffer[i] = 0; // Lime
            else if(ExtMA1Buffer[i] < ExtMA1Buffer[i - InpSmoothe1])
               ExtMA1ColorBuffer[i] = 1; // Red
            else
               ExtMA1ColorBuffer[i] = 2; // Aqua
           }
         else
            ExtMA1ColorBuffer[i] = 2; // Aqua
        }
      else
         ExtMA1ColorBuffer[i] = 2; // Aqua

      if(InpShowMA2 && InpCc2)
        {
         if(i > 1)
           {
            if(ExtMA2Buffer[i] > ExtMA2Buffer[i - 1])
               ExtMA2ColorBuffer[i] = 0; // Lime
            else if(ExtMA2Buffer[i] < ExtMA2Buffer[i - 1])
               ExtMA2ColorBuffer[i] = 1; // Red
            else
               ExtMA2ColorBuffer[i] = 2; // Aqua
           }
         else
            ExtMA2ColorBuffer[i] = 2; // Aqua
        }
      else
         ExtMA2ColorBuffer[i] = 2; // Aqua

      //--- Handle crosses
      if(InpShowMA2 && InpShowCross)
        {
         if(i > 0)
           {
            bool cross_up = ExtMA1Buffer[i] > ExtMA2Buffer[i] && ExtMA1Buffer[i - 1] <= ExtMA2Buffer[i - 1];
            bool cross_down = ExtMA1Buffer[i] < ExtMA2Buffer[i] && ExtMA1Buffer[i - 1] >= ExtMA2Buffer[i - 1];
            if(cross_up || cross_down)
              {
               ExtCrossBuffer[i] = ExtMA2Buffer[i];
              }
            else
               ExtCrossBuffer[i] = EMPTY_VALUE;
           }
        }
      else
         ExtCrossBuffer[i] = EMPTY_VALUE;

      //--- Handle bar highlights
      if(InpSpc1)
        {
         bool cross_up = open[i] < ExtMA1Buffer[i] && close[i] > ExtMA1Buffer[i];
         bool cross_down = open[i] > ExtMA1Buffer[i] && close[i] < ExtMA1Buffer[i];
         if(cross_up || cross_down)
            DrawHighlight(i, time[i], clrYellow);
        }
      if(InpShowMA2 && InpSpc2)
        {
         bool cross_up = open[i] < ExtMA2Buffer[i] && close[i] > ExtMA2Buffer[i];
         bool cross_down = open[i] > ExtMA2Buffer[i] && close[i] < ExtMA2Buffer[i];
         if(cross_up || cross_down)
            DrawHighlight(i, time[i], clrGold); // Different color for distinction
        }
     }
   
   // Update indicator label with current values
   UpdateIndicatorLabel();
     
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Calculate MA value based on type                                |
//+------------------------------------------------------------------+
double CalculateMA(double &temp_src[], ENUM_MA_TYPE type, int len, double factor)
  {
   double result = EMPTY_VALUE;
   double calc_array[];
   ArrayResize(calc_array, len);
   
   switch(type)
     {
      case SMA:
         SimpleMAOnBuffer(0, len-1, len, 0, temp_src, calc_array);
         result = calc_array[0];
         break;
      case EMA:
         ExponentialMAOnBuffer(0, len-1, len, 0, temp_src, calc_array);
         result = calc_array[0];
         break;
      case WMA:
         LinearWeightedMAOnBuffer(0, len-1, len, 0, temp_src, calc_array);
         result = calc_array[0];
         break;
      case RMA:
         SmoothedMAOnBuffer(0, len-1, len, 0, temp_src, calc_array);
         result = calc_array[0];
         break;
      case HullMA:
         result = HullMAOnArray(temp_src, len);
         break;
      case TEMA:
         result = TEMAOnArray(temp_src, len);
         break;
      case TilsonT3:
         result = TilsonT3OnArray(temp_src, len, factor);
         break;
      case VWMA:
         // VWMA needs volume data - this will be handled separately in GetMAValue
         result = EMPTY_VALUE;
         break;
     }
   
   return result;
  }

//+------------------------------------------------------------------+
//| Universal function to get MA value for a specific bar            |
//+------------------------------------------------------------------+
double GetMAValue(int bar, int rates_total, ENUM_TIMEFRAMES tf, ENUM_MA_TYPE type, int len, double factor, ENUM_APPLIED_PRICE price_type, const long &tick_volume[])
  {
   if(len <= 0) 
     {
      Print("GetMAValue: Invalid length ", len);
      return(0);
     }

//--- Handle VWMA separately as it needs volume data
   if(type == VWMA)
     {
      return GetVWMAValue(bar, rates_total, tf, len, price_type, tick_volume);
     }

//--- If using current timeframe, use simpler calculation
   if(tf == _Period)
     {
      // Use current timeframe data directly
      double source_data[];
      ArrayResize(source_data, rates_total);
      
      int copied = CopyPrice(Symbol(), tf, price_type, 0, rates_total, source_data);
      if(copied <= 0) 
        {
         Print("GetMAValue: Failed to copy price data for current TF. copied=", copied);
         return(EMPTY_VALUE);
        }
      
      // Check if we have enough data - we need len bars from bar-len+1 to bar
      if(bar < len - 1)
        {
         Print("GetMAValue: Not enough data for current TF. bar=", bar, " len=", len, " copied=", copied);
         return EMPTY_VALUE;
        }
      
      // Calculate MA directly - copy data from bar-len+1 to bar
      double temp_src[];
      ArrayResize(temp_src, len);
      
      // Copy data from the correct position (from bar-len+1 to bar)
      for(int i = 0; i < len; i++)
        {
         temp_src[i] = source_data[bar - len + 1 + i];
        }
      
      return CalculateMA(temp_src, type, len, factor);
     }

//--- Multi-timeframe calculation
//--- Prepare source data array - copy more data to ensure we have enough
   int bars_to_copy = rates_total + len; // Add extra bars to ensure we have enough data
   double source_data[];
   ArrayResize(source_data, bars_to_copy);

   int copied = CopyPrice(Symbol(), tf, price_type, 0, bars_to_copy, source_data);
   if(copied <= 0) 
     {
      Print("GetMAValue: Failed to copy price data. copied=", copied, " bars_to_copy=", bars_to_copy);
      return(EMPTY_VALUE);
     }
   
//--- Find corresponding bar shift in the target timeframe
   datetime current_bar_time[];
   CopyTime(Symbol(), _Period, bar, 1, current_bar_time);
   
   // Try to find the bar shift with better error handling
   int mtf_shift = iBarShift(Symbol(), tf, current_bar_time[0], false); // false = exact match not required
   
   if(mtf_shift < 0) 
     {
      // Try alternative approach - use the last available bar
      mtf_shift = copied - len;
      if(mtf_shift < 0)
        {
         Print("GetMAValue: Cannot find valid bar shift. mtf_shift=", mtf_shift, " bar=", bar, " copied=", copied);
         return EMPTY_VALUE;
        }
     }
   
   // Fix: Adjust the data range to ensure we have enough data
   int available_bars = copied - mtf_shift;
   if(available_bars < len) 
     {
      Print("GetMAValue: Not enough data. mtf_shift=", mtf_shift, " len=", len, " available_bars=", available_bars, " copied=", copied);
      return EMPTY_VALUE;
     }

   double result = EMPTY_VALUE;
   
//--- Array for calculation
   double calc_array[];
   ArrayResize(calc_array, len);
   
   // We need to reverse the array part for calculations
   double temp_src[];
   ArrayResize(temp_src, len);
   ArrayCopy(temp_src, source_data, 0, mtf_shift, len);
   ArraySetAsSeries(temp_src, true);

   result = CalculateMA(temp_src, type, len, factor);
     
   return result;
  }

//+------------------------------------------------------------------+
//| Copy Price Data Function                                         |
//+------------------------------------------------------------------+
int CopyPrice(string symbol, ENUM_TIMEFRAMES tf, ENUM_APPLIED_PRICE type, int start, int count, double &data[])
  {
   int copied = 0;
   if(type == PRICE_CLOSE) copied = CopyClose(symbol, tf, start, count, data);
   else if(type == PRICE_OPEN) copied = CopyOpen(symbol, tf, start, count, data);
   else if(type == PRICE_HIGH) copied = CopyHigh(symbol, tf, start, count, data);
   else if(type == PRICE_LOW) copied = CopyLow(symbol, tf, start, count, data);
   else
     {
      // For other price types, we need to calculate them manually
      double open[], high[], low[], close[];
      ArrayResize(open, count); ArrayResize(high, count); ArrayResize(low, count); ArrayResize(close, count);
      CopyOpen(symbol, tf, start, count, open);
      CopyHigh(symbol, tf, start, count, high);
      CopyLow(symbol, tf, start, count, low);
      CopyClose(symbol, tf, start, count, close);

      for(int i = 0; i < count; i++)
        {
         switch(type)
           {
            case PRICE_MEDIAN: data[i] = (high[i] + low[i]) / 2; break;
            case PRICE_TYPICAL: data[i] = (high[i] + low[i] + close[i]) / 3; break;
            case PRICE_WEIGHTED: data[i] = (high[i] + low[i] + close[i] * 2) / 4; break;
            default: data[i] = close[i]; break;
           }
        }
      copied = count;
     }
   ArraySetAsSeries(data, false); // Important: Copy... functions return series-like data
   
   return copied;
  }

//+------------------------------------------------------------------+
//| Custom MA implementations                                        |
//+------------------------------------------------------------------+
// TEMA on array
double TEMAOnArray(double &price[], int period)
  {
   if(ArraySize(price) < period * 3) return EMPTY_VALUE;
   double ema1[], ema2[], ema3[];
   ArrayResize(ema1, ArraySize(price));
   ArrayResize(ema2, ArraySize(price));
   ArrayResize(ema3, ArraySize(price));
   
   ExponentialMAOnBuffer(0, ArraySize(price)-1, period, 0, price, ema1);
   ExponentialMAOnBuffer(0, ArraySize(price)-1, period, 0, ema1, ema2);
   ExponentialMAOnBuffer(0, ArraySize(price)-1, period, 0, ema2, ema3);

   return 3 * ema1[0] - 3 * ema2[0] + ema3[0];
  }

// Hull MA on array
double HullMAOnArray(double &price[], int period)
  {
   int len1 = period / 2;
   int len2 = (int)MathSqrt(period);
   if(ArraySize(price) < period + len2) return EMPTY_VALUE;

   double wma1[], wma2[], wma_diff[];
   ArrayResize(wma1, ArraySize(price));
   ArrayResize(wma2, ArraySize(price));
   ArrayResize(wma_diff, ArraySize(price));
   
   LinearWeightedMAOnBuffer(0, ArraySize(price)-1, len1, 0, price, wma1);
   LinearWeightedMAOnBuffer(0, ArraySize(price)-1, period, 0, price, wma2);

   for(int i = 0; i < ArraySize(wma_diff); i++)
     {
      wma_diff[i] = 2 * wma1[i] - wma2[i];
     }
     
   double hull_ma[];
   ArrayResize(hull_ma, ArraySize(wma_diff));
   LinearWeightedMAOnBuffer(0, ArraySize(wma_diff)-1, len2, 0, wma_diff, hull_ma);

   return hull_ma[0];
  }
  
// Tilson T3 on Array
void GD(double &price[], int period, double factor, double &res[])
{
   double ema1[], ema2[];
   ArrayResize(ema1, ArraySize(price));
   ArrayResize(ema2, ArraySize(price));
   
   ExponentialMAOnBuffer(0, ArraySize(price)-1, period, 0, price, ema1);
   ExponentialMAOnBuffer(0, ArraySize(price)-1, period, 0, ema1, ema2);
   
   for(int i=0; i < ArraySize(price); i++)
   {
      res[i] = ema1[i]*(1+factor) - ema2[i]*factor;
   }
}

double TilsonT3OnArray(double &price[], int period, double factor)
{
   if(ArraySize(price) < period*6) return EMPTY_VALUE;

   double gd1[], gd2[], gd3[];
   ArrayResize(gd1, ArraySize(price));
   ArrayResize(gd2, ArraySize(price));
   ArrayResize(gd3, ArraySize(price));

   GD(price, period, factor, gd1);
   GD(gd1, period, factor, gd2);
   GD(gd2, period, factor, gd3);

   return gd3[0];
}

// VWMA on array
double VWMAOnArray(const double &price[], const long &volume[], int period)
{
   double sum_pv = 0;
   double sum_vol = 0;
   for(int i=0; i<period; i++)
   {
      sum_pv += price[i] * volume[i];
      sum_vol += volume[i];
   }
   return sum_vol > 0 ? sum_pv / sum_vol : price[0];
}

//+------------------------------------------------------------------+
//| Draw Highlight Function                                          |
//+------------------------------------------------------------------+
void DrawHighlight(int bar, datetime time, color clr)
  {
   string name = "CrossBG_" + IntegerToString(time);
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
     }
   ObjectSetInteger(0, name, OBJPROP_TIME, 0, time);
   ObjectSetDouble(0, name, OBJPROP_PRICE, 0, ChartGetDouble(0, CHART_PRICE_MIN));
   ObjectSetInteger(0, name, OBJPROP_TIME, 1, time - PeriodSeconds());
   ObjectSetDouble(0, name, OBJPROP_PRICE, 1, ChartGetDouble(0, CHART_PRICE_MAX));
  }

//+------------------------------------------------------------------+
//| Get VWMA value with volume data                                 |
//+------------------------------------------------------------------+
double GetVWMAValue(int bar, int rates_total, ENUM_TIMEFRAMES tf, int len, ENUM_APPLIED_PRICE price_type, const long &tick_volume[])
  {
   if(len <= 0) return(0);

//--- If using current timeframe, use simpler calculation
   if(tf == _Period)
     {
      // Use current timeframe data directly
      double source_data[];
      ArrayResize(source_data, rates_total);
      
      int copied = CopyPrice(Symbol(), tf, price_type, 0, rates_total, source_data);
      if(copied <= 0) return(EMPTY_VALUE);
      
      ArraySetAsSeries(source_data, true);
      
      // Get volume data
      long volume_data[];
      ArrayResize(volume_data, rates_total);
      CopyTickVolume(Symbol(), tf, 0, rates_total, volume_data);
      ArraySetAsSeries(volume_data, true);
      
      // Calculate VWMA
      double temp_src[];
      long temp_vol[];
      ArrayResize(temp_src, len);
      ArrayResize(temp_vol, len);
      ArrayCopy(temp_src, source_data, 0, bar, len);
      ArrayCopy(temp_vol, volume_data, 0, bar, len);
      
      return VWMAOnArray(temp_src, temp_vol, len);
     }

//--- Multi-timeframe VWMA calculation
   int bars_to_copy = rates_total + len;
   double source_data[];
   ArrayResize(source_data, bars_to_copy);

   int copied = CopyPrice(Symbol(), tf, price_type, 0, bars_to_copy, source_data);
   if(copied <= 0) return(EMPTY_VALUE);
   
   // Get volume data
   long mtf_volumes[];
   ArrayResize(mtf_volumes, bars_to_copy);
   CopyTickVolume(Symbol(), tf, 0, bars_to_copy, mtf_volumes);
   
   // Find corresponding bar shift
   datetime current_bar_time[];
   CopyTime(Symbol(), _Period, bar, 1, current_bar_time);
   int mtf_shift = iBarShift(Symbol(), tf, current_bar_time[0], false);
   
   if(mtf_shift < 0) 
     {
      mtf_shift = copied - len;
      if(mtf_shift < 0) return EMPTY_VALUE;
     }
   
   int available_bars = copied - mtf_shift;
   if(available_bars < len) return EMPTY_VALUE;

   // Prepare data for calculation
   double temp_src[];
   long temp_vol[];
   ArrayResize(temp_src, len);
   ArrayResize(temp_vol, len);
   ArrayCopy(temp_src, source_data, 0, mtf_shift, len);
   ArrayCopy(temp_vol, mtf_volumes, 0, mtf_shift, len);
   ArraySetAsSeries(temp_src, true);
   ArraySetAsSeries(temp_vol, true);

   return VWMAOnArray(temp_src, temp_vol, len);
  }
//+------------------------------------------------------------------+ 